/*************************************************
func: 封装socket服务端接口，实现tcp或udp通信
author: zyh
date: 2024.4.16
**************************************************/
#ifndef _SOCKET_SERVER_API_H_
#define _SOCKET_SERVER_API_H_

#ifdef __cplusplus
extern "C" {
#endif

#include <pthread.h>
#include <sys/types.h>//数据类型定义文件
#include <sys/socket.h>//提供socket函数及数结构

// 定义socket客户端信息结构体
typedef struct {
	int socket_fd;
	char ip[24];
	int port;
} client_data_t;

// 定义链表节点结构体
typedef struct client_node {
    client_data_t data;
    struct client_node* next;
} client_node_t;

// 定义链表结构体
typedef struct {
    client_node_t* head;
    client_node_t* tail;
    int count; // 节点计数
    pthread_mutex_t lock; // 互斥锁
} client_link_list_t;

//获取某个客户端信息，失败返回NULL
client_data_t* get_socket_client(client_link_list_t* list, int socket_fd);

//遍历打印链表中客户端信息
void print_socket_client_list(client_link_list_t* list);


typedef struct socket_server socket_server_t;
// 定义socket服务端信息结构体(内部自动处理上下线的客户端信息)
struct socket_server {
    /************配置服务端参数***********/
    /*
    服务器类型：
        0 为普通socket服务器（为每个客户端创建独立子线程）;
        1为基于epoll实现的socket服务（单线程处理多个客户端，IO多路复用，降低线程资源消耗）;
    */
    int type;
    
    /*
    通信字节流类型，其取值如：
    SOCK_STREAM：流式套接字（TCP方式）
    SOCK_DGRAM：数据包套接字（UDP方式）
    SOCK_RAM：原始套接字
    */
    int socket_type;//指明通信字节流类型，默认SOCK_STREAM
    int socket_protocol;//表示传输协议，常用的有 IPPROTO_TCP 和IPPROTO_UDP，分别表示 TCP 传输协议和 UDP 传输协议。如果为0，协议将自动选择。
    int broadcast_flag;//发送UDP数据通信时是否广播，一般是tcp通信，所以一般设置为0
	const char *ip;//服务器地址："127.0.0.1"或"0.0.0.0"
    int bind_port;//绑定的本地端口
	int max_listen_backlog;//socket监听客户端连接套接口排队的最大连接个数，如果为0，内部默认128
	int max_client_online_num;//在线客户端的最大数量，如果为0，内部默认128
    int rcv_buf_size;//设置接收数据缓冲区大小，如果为0，内部默认1024

	void *arg;//留给外部的自定义传参
	void (*client_onLogin)(socket_server_t *server, client_data_t client);//客户端登录的回调
	void (*client_onMessage)(socket_server_t *server, client_data_t client, void *buf, int len);//接收处理客户端消息的回调
	void (*client_onExit)(socket_server_t *server, client_data_t client);//客户端断开连接的回调

	/*在线客户端信息(如需主动发送消息给客户端，则可以通过链表客户端消息里面的通信句柄发送)*/
	client_link_list_t client_list;//链表方式动态存储在线客户端信息，节省内存空间
	
	//内部运行状态变量---------	
	pthread_t thread_id;
	int run_flag;//本服务启动标志：1启动，0未启动或已退出
};

//启动一个socket服务端
int start_socket_server(socket_server_t *server);
int close_socket_server(socket_server_t *server);
int restart_socket_server(socket_server_t *server);

#if 0 //test
socket_server_t server1 = {
    .type = 0,
    .socket_type = SOCK_STREAM,
    .socket_protocol = 0,
    .broadcast_flag = 0,
    .ip = "0.0.0.0",
    .bind_port = 0,
    .max_listen_backlog = 32,
    .max_client_online_num = 128,
    .rcv_buf_size = 0,

    .client_onLogin = NULL,
    .client_onMessage = NULL,
    .client_onExit = NULL
};
#endif 

#ifdef __cplusplus
}
#endif

#endif
